package dht

import (
	"encoding/binary"
	"errors"
	"io"
)

type Msg struct {
	mtype  MsgType
	length int
	id     ID
	data   []byte
}

func (m Msg) Read(r io.Reader) (Msg, error) {
	ret := Msg{}
	tmp := int32(0)
	err := binary.Read(r, binary.BigEndian, &tmp)
	if err != nil {
		return ret, err
	}
	ret.mtype = MsgType(tmp)
	err = binary.Read(r, binary.BigEndian, &tmp)
	if err != nil {
		return ret, err
	}
	ret.length = int(tmp)
	buf := make([]byte, 20)
	n, err := r.Read(buf)
	if err != nil {
		return ret, err
	}
	if n != len(buf) {
		return ret, errors.New("msg read id length error")
	}
	id, _ := FromID(buf)
	ret.id = id
	if ret.length == 0 {
		return ret, nil
	}
	buf = make([]byte, ret.length)
	n, err = r.Read(buf)
	if err != nil {
		return ret, err
	}
	if n != len(buf) {
		return ret, errors.New("msg read data length error")
	}
	ret.data = buf
	return ret, nil
}

func (m Msg) Write(w io.Writer, id ID, mtype MsgType, data []byte) error {
	tmp := int32(mtype)
	err := binary.Write(w, binary.BigEndian, tmp)
	if err != nil {
		return err
	}
	tmp = int32(len(data))
	err = binary.Write(w, binary.BigEndian, tmp)
	if err != nil {
		return err
	}
	n, err := w.Write(id[:])
	if err != nil {
		return err
	}
	if n != len(id) {
		return errors.New("msg write id length error")
	}
	n, err = w.Write(data)
	if err != nil {
		return err
	}
	if n != len(data) {
		return errors.New("msg write data length error")
	}
	return nil
}

func (m Msg) WriteError(w io.Writer, id ID, err error) error {
	return m.Write(w, id, 0, []byte(err.Error()))
}

func (m Msg) Type() MsgType {
	return m.mtype
}

func (m Msg) ID() ID {
	return m.id
}

func (m Msg) Data() []byte {
	return m.data
}
